/**
 * @file custom_types.cpp
 * @brief Example using custom types with the tree
 */

#include "../complete_binary_tree.hpp"
#include <iostream>
#include <string>
#include <memory>

// Custom leaf type
struct DataBlock {
    std::string id;
    int value;
    
    DataBlock() : id("default"), value(0) {}
    DataBlock(std::string i, int v) : id(std::move(i)), value(v) {}
    
    // Must be nothrow move constructible
    DataBlock(DataBlock&&) noexcept = default;
    DataBlock& operator=(DataBlock&&) noexcept = default;
    
    // Regular copy operations
    DataBlock(const DataBlock&) = default;
    DataBlock& operator=(const DataBlock&) = default;
    
    friend std::ostream& operator<<(std::ostream& os, const DataBlock& db) {
        return os << "{id:" << db.id << ", value:" << db.value << "}";
    }
};

// Custom internal node type
struct MetaNode {
    size_t level;
    std::string label;
    
    MetaNode() : level(0), label("unlabeled") {}
    MetaNode(size_t l, std::string lab) : level(l), label(std::move(lab)) {}
    
    // Must be nothrow move constructible
    MetaNode(MetaNode&&) noexcept = default;
    MetaNode& operator=(MetaNode&&) noexcept = default;
    
    // Regular copy operations
    MetaNode(const MetaNode&) = default;
    MetaNode& operator=(const MetaNode&) = default;
    
    friend std::ostream& operator<<(std::ostream& os, const MetaNode& mn) {
        return os << "{level:" << mn.level << ", label:" << mn.label << "}";
    }
};

int main() {
    std::cout << "=== Complete Binary Tree - Custom Types Example ===\n\n";

    // Create tree with custom types
    CompleteBinaryTree<DataBlock, MetaNode> tree(8);
    
    std::cout << "Created tree with custom types\n";
    std::cout << "Leaf type: DataBlock (id + value)\n";
    std::cout << "Internal type: MetaNode (level + label)\n\n";

    // Initialize leaves with custom data
    std::cout << "=== Initializing Leaves ===\n";
    for (size_t i = 0; i < tree.leaf_count(); ++i) {
        tree[i] = DataBlock("Block_" + std::to_string(i), static_cast<int>(i * 100));
    }
    
    for (size_t i = 0; i < tree.leaf_count(); ++i) {
        std::cout << "Leaf " << i << ": " << tree[i] << "\n";
    }

    // Initialize internal nodes
    std::cout << "\n=== Initializing Internal Nodes ===\n";
    for (size_t i = 0; i < tree.internal_count(); ++i) {
        size_t level = 0;
        size_t temp = i + 1;
        while (temp > 0) {
            temp >>= 1;
            level++;
        }
        tree.internal_at(i) = MetaNode(level, "Node_" + std::to_string(i));
    }
    
    for (size_t i = 0; i < tree.internal_count(); ++i) {
        std::cout << "Internal " << i << ": " << tree.internal_at(i) << "\n";
    }

    // Demonstrate path operations with custom types
    std::cout << "\n=== Path to Root (Leaf 5) ===\n";
    std::cout << "Starting at: " << tree[5] << "\n";
    
    auto path = tree.path_to_root(5);
    for (size_t i = 0; i < path.size(); ++i) {
        std::cout << "  Step " << i + 1 << ": " << path[i].get() << "\n";
    }

    // Modify data along path
    std::cout << "\n=== Modifying Path ===\n";
    tree.for_each_on_path(7, [](MetaNode& node) {
        node.label += "_modified";
    });
    
    std::cout << "Root after modification: " << tree.internal_at(0) << "\n";

    // Use for_each with custom types
    std::cout << "\n=== Using for_each_leaf ===\n";
    tree.for_each_leaf([](DataBlock& block) {
        block.value *= 2;
    });
    
    std::cout << "After doubling values:\n";
    for (size_t i = 0; i < std::min(size_t(4), tree.leaf_count()); ++i) {
        std::cout << "  Leaf " << i << ": " << tree[i] << "\n";
    }

    // Demonstrate resize with custom types
    std::cout << "\n=== Resize Operations ===\n";
    std::cout << "Original leaf count: " << tree.leaf_count() << "\n";
    std::cout << "First leaf before resize: " << tree[0] << "\n";
    
    tree.resize(4);
    std::cout << "After resize to 4:\n";
    std::cout << "  Leaf count: " << tree.leaf_count() << "\n";
    std::cout << "  First leaf preserved: " << tree[0] << "\n";
    
    tree.resize(12);
    std::cout << "After resize to 12:\n";
    std::cout << "  Leaf count: " << tree.leaf_count() << "\n";
    std::cout << "  New leaf (index 10): " << tree[10] << "\n";

    // Demonstrate swap
    std::cout << "\n=== Swap Operation ===\n";
    CompleteBinaryTree<DataBlock, MetaNode> tree2(3);
    for (size_t i = 0; i < tree2.leaf_count(); ++i) {
        tree2[i] = DataBlock("Tree2_" + std::to_string(i), static_cast<int>(i));
    }
    
    std::cout << "Tree1 leaf count before swap: " << tree.leaf_count() << "\n";
    std::cout << "Tree2 leaf count before swap: " << tree2.leaf_count() << "\n";
    
    tree.swap(tree2);
    
    std::cout << "Tree1 leaf count after swap: " << tree.leaf_count() << "\n";
    std::cout << "Tree2 leaf count after swap: " << tree2.leaf_count() << "\n";
    std::cout << "Tree1 first leaf: " << tree[0] << "\n";

    // Integrity validation
    std::cout << "\n=== Integrity Validation ===\n";
    if (tree.validate_integrity() && tree2.validate_integrity()) {
        std::cout << "✓ Both trees passed integrity check\n";
    }

    std::cout << "\n=== Example Complete ===\n";
    return 0;
}
